
jwTable.translations = {
    insert_row:         "Insert row",
    insert_column:      "Insert column",
    delete_row:         "Delete row",
    delete_column:      "Delete column",
    suffix_before:      "before",
    suffix_after:       "after"
}

jwTable.instances = new Object();
jwTable.lastIndex = 1;

jwTable.getById = function( _id )
{
    return jwTable.instances[ _id ];
}

function jwTable( _domElement, _options, _data )
{
    var pri_id = jwTable.lastIndex++;
    jwTable.instances[ pri_id ] = this;
    
    this.currentPos = new Object();
    
    this.id = function()
    {
        return pri_id;
    }
    
    this.colRow = true;
    
    this.cells = new Object();    
    
    if ( _data )
        this.initFromData( _domElement, _data );
    else
        this.initFromDom( _domElement );
        
    this.objectHolder = document.createElement( 'INPUT' );
    this.objectHolder.style.position = "absolute";
    this.objectHolder.style.marginLeft = "-5000px";
    
    this.tableElement.parentNode.insertBefore( this.objectHolder, this.tableElement );
    this.tableElement.parentNode.insertBefore( this.focusElement, this.tableElement );
        
    if ( _options && _options['name'] )
        this.objectHolder.name = _options['name'];
    else
        this.objectHolder.name = "tmp_" + this.id();
        
    this.serialize();
}

jwTable.prototype.initFromDom = function( _domElement )
{
    var copy = this;
    if( _domElement.nodeName != "TABLE" )
        throw( "The given domElement is not a TABLE" );

    this.tableElement = _domElement;
    var copy = this;
    
    this.focusElement = document.createElement( 'INPUT' );
    this.focusElement.style.position = "absolute";
    this.focusElement.style.marginLeft = "-5000px";    
    
    if( this.tableElement.addEventListener )
    {
        this.tableElement.addEventListener( 'mouseover', function( event ){ copy.EventListeners.handleMouseMove.call( copy, event ) }, false );
        this.tableElement.addEventListener( 'mouseout', function( event ){ copy.EventListeners.handleMouseOut.call( copy, event ) }, false );
        this.tableElement.addEventListener( 'contextmenu', function( event ){ copy.EventListeners.handleContext.call( copy, event ) }, false );
//        this.focusElement.addEventListener( 'keydown', function( event ){ copy.EventListeners.handleKeyDown.call( copy, event  ) },false );
//        this.focusElement.addEventListener( 'keypress', function( event ){ event.preventDefault(); },false );
    }
    else if( this.tableElement.attachEvent )
    {
        this.tableElement.attachEvent( 'onmouseover', function( event ){ copy.EventListeners.handleMouseMove.call( copy, event ) } );
        this.tableElement.attachEvent( 'onmouseout', function( event ){ copy.EventListeners.handleMouseOut.call( copy, event ) } );
        this.tableElement.attachEvent( 'oncontextmenu', function( event ){ copy.EventListeners.handleContext.call( copy, event ) } );
//        this.focusElement.attachEvent( 'onkeydown', function( event ){ copy.EventListeners.handleKeyDown.call( copy, event ) } );
//        this.focusElement.attachEvent( 'onkeypress', function( event ){ window.event.returnValue = false; } );                
    }
    
    var rows = this.tableElement.getElementsByTagName( 'TR' );
    for ( var i = 0; i < rows.length; i++ )
    {
        var children = rows[i].childNodes;
        var j = 0;
        for ( var k = 0; k < children.length; k++ )
        {
            if( children[k].nodeName.search( /td/i ) > -1 || children[k].nodeName.search( /th/i ) > -1 )
            {
                var tmp = new jwCell( children[k], { callbackSave : function(){ copy.serialize(); } } );
                this.setCell( j, i, tmp );
                j++;
            }
        }
    }
}

jwTable.prototype.setCell = function( _col, _row, _cell )
{   
    if ( !this.cells[ _col ] )
    {
        this.cells[ _col ] = new Object();
    }
    
    this.cells[ _col ][ _row ] = _cell;
    
    if ( !this.colRow )
    {
        var tmp = _col;
        _col = _row;
        _row = tmp;
    }   
    _cell.rowIndex = _row;
    _cell.colIndex = _col;
}

jwTable.prototype.addJsRow = function( _pos, _flag )
{
    var index = "colIndex";
    if ( typeof( _flag ) == "undefined" )
    {
        _flag = true;
    }
        
    if ( this.colRow == _flag )
    {
        this.swapAxes();
        index = "rowIndex";
    }
    
    var max = 0;
    
    for ( var i in this.cells )
    {
        if ( parseInt( max ) < i )
            max = i;
    }
    
    var lastPos = max - -1;    
    for ( var i = max; i >= parseInt( _pos ); i-- )
    {
        if ( this.cells[i] )
        {
            // cells[i] exists
            this.cells[ i - -1 ] = this.cells[i];
            
            for ( var j in this.cells[ i - -1 ] )
            {
                this.cells[ i - -1 ][ j ][index] = i - -1;
            }
            
            delete this.cells[i];
        }
    }

//    this.debug( document.getElementById( 'debugx' ) );    

    if ( this.colRow != _flag )
    {
        this.swapAxes();   
    }
}


jwTable.prototype.addRow = function( _pos )
{
    var copy = this;
    var parent;
    if ( !this.colRow )
    {
        this.swapAxes();
        return this.addRow( _pos );
    }
   
    this.addJsRow( _pos );

    var newRow = document.createElement( "TR" );        
        
    if ( this.cells[ 0 ][ _pos - -1 ] )
    {
        parent = this.cells[ 0 ][ _pos - -1 ].domElement.parentNode;
        parent.parentNode.insertBefore( newRow, parent );
    }
    else if ( this.cells[ 0 ][ _pos - 1 ].domElement )
    {
        parent = this.cells[ 0 ][ _pos - 1 ].domElement.parentNode;
        parent.parentNode.appendChild( newRow );
    }
    
    for ( var i in this.cells )
    {
        if ( i == 0 )
            lastTd = document.createElement( 'TH' );
        else
            lastTd = document.createElement( 'TD' );
            
        lastTd.innerHTML = "&nbsp;";
        newRow.appendChild( lastTd );
        this.setCell( i, _pos, new jwCell( lastTd, { callbackSave : function(){ copy.serialize(); } } ) );
    }
    
//    this.debug( document.getElementById( 'debugx' ), "addedRow" );
    this.serialize();
    return true;
}

jwTable.prototype.removeRow = function( _pos )
{
    var parent = this.cells[ 0 ][ _pos ].domElement.parentNode;
    if ( this.colRow )
        this.swapAxes();
        
    delete this.cells[ _pos ];
    
    var newPos = parseInt( _pos ) + 1;
    
    
    for ( var i in this.cells )
    {     
        if ( i == newPos )
        {
            this.cells[ newPos - 1 ] = this.cells[ newPos ];
            for ( var j in this.cells[ newPos - 1 ] )
            {
                this.cells[ newPos - 1 ][ j ].rowIndex = newPos - 1;
            }
            delete this.cells[ newPos ];
            newPos++; 
        }
    }
    
    if ( !this.colRow )
        this.swapAxes();
    
    parent.parentNode.removeChild( parent );
    this.serialize();
}

jwTable.prototype.addCol = function( _pos )
{
    var copy = this;
    if( !this.colRow)
    {
        this.swapAxes();
        return this.addCol( _pos );
    }
    
    this.addJsRow( _pos, false );
    
    for( var i in this.cells )
    {
        if ( i == 0 )
            lastTd = document.createElement( 'TH' );
        else
            lastTd = document.createElement( 'TD' );

        lastTd.innerHTML = "&nbsp;";
        
        if( this.cells[i][ _pos - -1 ] )
        {
            var before = this.cells[i][ _pos - -1 ].domElement;
            before.parentNode.insertBefore( lastTd, before );
        }
        else if ( this.cells[i][ _pos - 1 ] ) 
        {
            var after = this.cells[i][ _pos - 1 ].domElement;
            after.parentNode.appendChild( lastTd, before );            
            
        }
        this.setCell( i, _pos, new jwCell( lastTd, { callbackSave : function(){ copy.serialize(); } } ) );            
    }
//    this.debug( document.getElementById( 'debugx' ), "added column" );
    this.serialize;
    return true;
}

jwTable.prototype.removeCol = function( _pos )
{

    for ( var j in this.cells[ _pos ] )
    {
        this.cells[ _pos ][j].domElement.parentNode.removeChild( this.cells[ _pos ][j].domElement );
    }
    
    delete this.cells[ _pos ];
    
    var newPos = parseInt( _pos ) + 1;
    
    for ( var i in this.cells )
    {     
        if ( i == newPos )
        {
            this.cells[ newPos - 1 ] = this.cells[ newPos ];
            for ( var j in this.cells[ newPos - 1 ] )
            {
                this.cells[ newPos - 1 ][ j ].colIndex = newPos - 1;
            }
            delete this.cells[ newPos ];
            newPos++; 
        }
    }
    this.serialize();
}


jwTable.prototype.EventListeners = {
    handleMouseMove : function( _event )
    {
        if( window.event && LastEvent )
        {
            _event.rowIndex = LastEvent.rowIndex;
            _event.colIndex = LastEvent.colIndex;
            _event.target = _event.srcElement;
        }
        
        if ( !this.colRow )
            this.swapAxes();
            
        if( _event.target.nodeName.search( /td/i ) > -1 || _event.target.nodeName.search( /th/i ) > -1 )
        {
            var cell = this.cells[ _event.colIndex ][ _event.rowIndex ];
            if( cell.domElement.className.search( "highlight") == -1 )
                cell.domElement.className += " highlight";
        }
        this.currentPos = { row: _event.rowIndex, col: _event.colIndex };
    },
    handleMouseOut : function( _event )
    {
        if( window.event && LastEvent )
        {
            _event.rowIndex = LastEvent.rowIndex;
            _event.colIndex = LastEvent.colIndex;
            _event.target = _event.srcElement;
        }        
        
        if ( !this.colRow )
            this.swapAxes();
            
        if( _event.target.nodeName.search( /td/i ) > -1 || _event.target.nodeName.search( /th/i ) > -1 )
        {
            var cell = this.cells[ _event.colIndex ][ _event.rowIndex ];
            cell.domElement.className = cell.domElement.className.replace( /\s.*?highlight/, "" );
        }
    },
    handleContext : function( _event )
    {
        if( window.event && LastEvent )
        {
            _event.rowIndex = LastEvent.rowIndex;
            _event.colIndex = LastEvent.colIndex;
            _event.target = _event.srcElement;
        }        
        
        if ( window.scrollX || window.scrollY )
        {
            offsetX = parseInt( window.scrollX );
            offsetY = parseInt( window.scrollY );
        }
        else if ( window.pageXOffset || window.pageYOffset )
        {
            offsetX = parseInt( window.pageXOffset );
            offsetY = parseInt( window.pageYOffset );
        }        
        
        if ( !this.colRow )
            this.swapAxes();
            
        var cellElement;            
            
        if ( typeof( _event.rowIndex ) != "undefined" )    
            cellElement = this.cells[ _event.colIndex ][ _event.rowIndex ].domElement;            
         
        var suffix = " "+jwTable.translations["suffix_before"];
            
        if( _event.rowIndex != 0 && _event.colIndex == 0 )
        {
            
            var indexSuff = 0;
            var offset = getCumulativeOffset( this.cells[ 0 ][ _event.rowIndex ].domElement );
            
            
            if( _event.clientY - -offsetY - offset.y  > cellElement.offsetHeight / 2 )
            {
                suffix = " after";
                indexSuff  = 1;
            }
            jwContext.getInstance().buildUp( [
                { label: jwTable.translations["insert_row"] + suffix, css: 'icon-table-add-row', action: "jwTable.getById('" + this.id() + "').addRow('" + ( _event.rowIndex - -indexSuff ) +"')" },
                { label: jwTable.translations["delete_row"], css: 'icon-table-delete-row', action: "jwTable.getById('" + this.id() + "').removeRow('" + _event.rowIndex + "')" }
            ] ).show( _event );
            
        }
        else if (  _event.rowIndex == 0 && _event.colIndex != 0  )
        {
            var indexSuff = 0;
            var offset = getCumulativeOffset( this.cells[ _event.colIndex ][ 0 ].domElement );
            if( _event.clientX - -offsetX - offset.x  > cellElement.offsetWidth / 2 )
            {
                suffix = " after";
                indexSuff  = 1;
            }            
            jwContext.getInstance().buildUp( [
                { label: jwTable.translations["insert_column"] + suffix, css: 'icon-table-add-col', action: "jwTable.getById('" + this.id() + "').addCol('" + ( _event.colIndex - -indexSuff ) + "')" },
                { label: jwTable.translations["delete_column"], css: 'icon-table-delete-col', action: "jwTable.getById('" + this.id() + "').removeCol('" + _event.colIndex + "')" }
            ] ).show( _event );
        }
        if ( _event.preventDefault )
            _event.preventDefault();
        else
            window.event.returnValue = false;
    },
    handleKeyDown : function( _event )
    {
        var state = this.colRow;
            
        switch( _event.keyCode )
        {
            case 40:
                var state = this.colRow;
                if ( this.colRow )
                    this.swapAxes();
                    
                if( this.cells[ this.currentPos.row - -1 ] )
                    this.currentPos = { col: this.currentPos.col, row: this.currentPos.row - -1 };
                    
                var cell = this.cells[ this.currentPos.row ][ this.currentPos.col ];
                if( cell.domElement.className.search( "highlight") == -1 )
                    cell.domElement.className += " highlight";
                //cell.domElement.className = cell.domElement.className.replace( /\s.*?highlight/, "" );
                
                if ( state != this.colRow )
                    this.swapAxes();
            break;
            case 38:
                if( this.cells[ this.currentPos.row - 1 ] )
                    this.currentPos = { col: this.currentPos.col, row: this.currentPos.row - 1 };
            break;
            case 13:
                this.cells[ this.currentPos.col ][ this.currentPos.row ].edit();
                if ( _event.preventDefault )
                    _event.preventDefault();
                else
                    window.event.cancelBubble = true;
            break;
        }
        
        if ( this.colRw != this.state )
            this.swapAxes;
    }
}

/**
 * Swaps the columns with the rows
 */
jwTable.prototype.swapAxes = function()
{
    var replace = new Object();
    
    for ( var i in this.cells )
    {
        for( var j in this.cells[i] )
        {
            if( !replace[j] )
                replace[j] = new Object();
            replace[j][i] = this.cells[i][j];
        }
    }
    delete this.cells;
    this.cells = replace;
    this.colRow = !this.colRow;
}

/**
 * Converts the jwTable into a json format string
 * @returns {String} The json representation of the table
 */
jwTable.prototype.serialize = function()
{
    //this.focusElement.focus();
    var state = this.colRow;
    if( this.colRow )
        this.swapAxes();
    
    var jsonForm = "{";
    
    for( var i in this.cells )
    {
        
        jsonForm += "'" + i + "':{";
        for( var j in this.cells[i] )
        {
            jsonForm += "'" + j + "': '"+this.cells[i][j].domElement.innerHTML.trim() + "' ," ;
        }
        jsonForm += "},";
    }
    jsonForm += "}";
    this.objectHolder.value = jsonForm;
    if ( state != this.colRow )
        this.swapAxes;
        
}

jwTable.prototype.debug = function( _element, _msg )
{
    var state = this.colRow;
    if ( this.colRow )
        this.swapAxes();
    
    var dbg = "<table>";
    
    for ( var i in this.cells )
    {
        dbg += "<tr>";    
        for ( var j in this.cells[i] )
        {
            if ( i == 0 || j == 0 )
                dbg += "<th>";
            else
                dbg += "<td>";

            dbg += this.cells[i][j].colIndex + ", " + this.cells[i][j].rowIndex;
            if ( i == 0 || j == 0 )
                dbg += "</th>";
            else
                dbg += "</td>";
            
        }
        dbg += "</tr>";
    }
    
    dbg += "</table>";
    _element.innerHTML += dbg;
    if ( _msg )
        _element.innerHTML += _msg + "<br/>";
    
    if ( this.colRow != state )
        this.swapAxes();    
}